GString *string);
GskTransform * (* apply) (GskTransform *transform,
GskTransform *apply_to);
+ GskTransform * (* invert) (GskTransform *transform,
+ GskTransform *next);
/* both matrices have the same type */
gboolean (* equal) (GskTransform *first_transform,
GskTransform *second_transform);
gsk_identity_transform_apply (GskTransform *transform,
GskTransform *apply_to)
{
- return apply_to;
+ /* We do the following to make sure inverting a non-NULL transform
+ * will return a non-NULL transform.
+ */
+ if (apply_to)
+ return apply_to;
+ else
+ return gsk_transform_new ();
+}
+
+static GskTransform *
+gsk_identity_transform_invert (GskTransform *transform,
+ GskTransform *next)
+{
+ /* We do the following to make sure inverting a non-NULL transform
+ * will return a non-NULL transform.
+ */
+ if (next)
+ return next;
+ else
+ return gsk_transform_new ();
}
static gboolean
gsk_identity_transform_apply_translate,
gsk_identity_transform_print,
gsk_identity_transform_apply,
+ gsk_identity_transform_invert,
gsk_identity_transform_equal,
};
self->category);
}
+static GskTransform *
+gsk_matrix_transform_invert (GskTransform *transform,
+ GskTransform *next)
+{
+ GskMatrixTransform *self = (GskMatrixTransform *) transform;
+ graphene_matrix_t inverse;
+
+ if (!graphene_matrix_inverse (&self->matrix, &inverse))
+ {
+ gsk_transform_unref (next);
+ return NULL;
+ }
+
+ return gsk_transform_matrix_with_category (next,
+ &inverse,
+ self->category);
+}
+
static gboolean
gsk_matrix_transform_equal (GskTransform *first_transform,
GskTransform *second_transform)
gsk_matrix_transform_apply_translate,
gsk_matrix_transform_print,
gsk_matrix_transform_apply,
+ gsk_matrix_transform_invert,
gsk_matrix_transform_equal,
};
return gsk_transform_translate_3d (apply_to, &self->point);
}
+static GskTransform *
+gsk_translate_transform_invert (GskTransform *transform,
+ GskTransform *next)
+{
+ GskTranslateTransform *self = (GskTranslateTransform *) transform;
+
+ return gsk_transform_translate_3d (next, &GRAPHENE_POINT3D_INIT (-self->point.x, -self->point.y, -self->point.z));
+}
+
static gboolean
gsk_translate_transform_equal (GskTransform *first_transform,
GskTransform *second_transform)
gsk_translate_transform_apply_translate,
gsk_translate_transform_print,
gsk_translate_transform_apply,
+ gsk_translate_transform_invert,
gsk_translate_transform_equal,
};
return gsk_transform_rotate (apply_to, self->angle);
}
+static GskTransform *
+gsk_rotate_transform_invert (GskTransform *transform,
+ GskTransform *next)
+{
+ GskRotateTransform *self = (GskRotateTransform *) transform;
+
+ return gsk_transform_rotate (next, - self->angle);
+}
+
static gboolean
gsk_rotate_transform_equal (GskTransform *first_transform,
GskTransform *second_transform)
gsk_rotate_transform_apply_translate,
gsk_rotate_transform_print,
gsk_rotate_transform_apply,
+ gsk_rotate_transform_invert,
gsk_rotate_transform_equal,
};
return gsk_transform_rotate_3d (apply_to, self->angle, &self->axis);
}
+static GskTransform *
+gsk_rotate3d_transform_invert (GskTransform *transform,
+ GskTransform *next)
+{
+ GskRotate3dTransform *self = (GskRotate3dTransform *) transform;
+
+ return gsk_transform_rotate_3d (next, - self->angle, &self->axis);
+}
+
static gboolean
gsk_rotate3d_transform_equal (GskTransform *first_transform,
GskTransform *second_transform)
gsk_rotate3d_transform_apply_translate,
gsk_rotate3d_transform_print,
gsk_rotate3d_transform_apply,
+ gsk_rotate3d_transform_invert,
gsk_rotate3d_transform_equal,
};
return gsk_transform_scale_3d (apply_to, self->factor_x, self->factor_y, self->factor_z);
}
+static GskTransform *
+gsk_scale_transform_invert (GskTransform *transform,
+ GskTransform *next)
+{
+ GskScaleTransform *self = (GskScaleTransform *) transform;
+
+ return gsk_transform_scale_3d (next,
+ 1.f / self->factor_x,
+ 1.f / self->factor_y,
+ 1.f / self->factor_z);
+}
+
static gboolean
gsk_scale_transform_equal (GskTransform *first_transform,
GskTransform *second_transform)
gsk_scale_transform_apply_translate,
gsk_scale_transform_print,
gsk_scale_transform_apply,
+ gsk_scale_transform_invert,
gsk_scale_transform_equal,
};
return other->transform_class->apply (other, next);
}
+/**
+ * gsk_transform_invert:
+ * @self: (allow-none) (transfer full): Transform to invert
+ *
+ * Inverts the given transform.
+ *
+ * If @self is not invertible, %NULL is returned.
+ * Note that inverting %NULL also returns %NULL, which is
+ * the correct inverse of %NULL. If you need to differentiate
+ * between those cases, you should check @self is not %NULL
+ * before calling this function.
+ *
+ * Returns: The inverted transform or %NULL if the transform
+ * cannot be inverted.
+ **/
+GskTransform *
+gsk_transform_invert (GskTransform *self)
+{
+ GskTransform *result = NULL;
+ GskTransform *cur;
+
+ for (cur = self; cur; cur = cur->next)
+ {
+ result = cur->transform_class->invert (cur, result);
+ if (result == NULL)
+ break;
+ }
+
+ gsk_transform_unref (self);
+
+ return result;
+}
+
/**
* gsk_transform_equal:
* @first: the first matrix
#include <gtk/gtk.h>
-#define EPSILON (1.f / 1024 / 1024)
+#define EPSILON (1.f / 1024 / 32) /* 2^-15 */
/* macros stolen from graphene testsuite, so they get to keep their names */
} \
} G_STMT_END
+#define graphene_assert_fuzzy_transform_equal(t1,t2,epsilon) \
+ G_STMT_START { \
+ graphene_matrix_t __mat1, __mat2; \
+ gsk_transform_to_matrix ((t1), &__mat1); \
+ gsk_transform_to_matrix ((t2), &__mat2); \
+ graphene_assert_fuzzy_matrix_equal (&__mat1, &__mat2, (epsilon)); \
+ } G_STMT_END
+
static struct {
GskTransformCategory category;
} test_transforms[] = {
}
}
+static void
+test_invert (void)
+{
+ GskTransform *transform, *inverse, *identity;
+ guint i, j, k;
+
+ for (i = 0; i < G_N_ELEMENTS (test_transforms); i++)
+ {
+ for (j = 0; j < G_N_ELEMENTS (test_transforms); j++)
+ {
+ for (k = 0; k < G_N_ELEMENTS (test_transforms); k++)
+ {
+ transform = apply_test_transform (NULL, i);
+ transform = apply_test_transform (transform, j);
+ transform = apply_test_transform (transform, k);
+ inverse = gsk_transform_invert (gsk_transform_ref (transform));
+ g_assert (inverse != NULL || transform == NULL);
+
+ identity = gsk_transform_transform (gsk_transform_ref (transform), inverse);
+ graphene_assert_fuzzy_transform_equal (identity, NULL, EPSILON);
+ gsk_transform_unref (identity);
+
+ inverse = gsk_transform_invert (inverse);
+ graphene_assert_fuzzy_transform_equal (transform, inverse, EPSILON);
+
+ gsk_transform_unref (transform);
+ gsk_transform_unref (inverse);
+ }
+ }
+ }
+}
+
int
main (int argc,
char *argv[])
g_test_add_func ("/transform/conversions/simple", test_conversions_simple);
g_test_add_func ("/transform/conversions/transformed", test_conversions_transformed);
+ g_test_add_func ("/transform/invert", test_invert);
return g_test_run ();
}